package com.cloudsea.yunhaistores.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cloudsea.yunhaistores.entity.Attributes;
import com.cloudsea.yunhaistores.entity.Skus;
import com.cloudsea.yunhaistores.entity.Spus;
import com.cloudsea.yunhaistores.mapper.AttributesMapper;
import com.cloudsea.yunhaistores.mapper.SkusMapper;
import com.cloudsea.yunhaistores.mapper.SpusMapper;
import com.cloudsea.yunhaistores.service.MenuService;
import com.cloudsea.yunhaistores.vo.MenuVO;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class MenuServiceImpl implements MenuService {

    private static final Logger log = LoggerFactory.getLogger(MenuServiceImpl.class);


    @Autowired private SpusMapper spusMapper;
    @Autowired private SkusMapper skusMapper;
    @Autowired private AttributesMapper attributesMapper;

    @Autowired private StringRedisTemplate redisTemplate;
    @Autowired private ObjectMapper objectMapper;

    // 缓存常量
    private static final String MENU_CACHE_KEY_PREFIX = "menu:store:";
    private static final long MENU_CACHE_TTL_MINUTES = 30; // 缓存菜单30分钟

    @Override
    public MenuVO getMenuByStoreId(Long storeId) {
        // 1. 定义缓存的Key
        String cacheKey = MENU_CACHE_KEY_PREFIX + storeId;

        // 2. 尝试先从Redis缓存中获取
        try {
            String cachedMenuJson = redisTemplate.opsForValue().get(cacheKey);
            if (StringUtils.hasText(cachedMenuJson)) {
                log.info("菜单缓存命中，门店ID: {}", storeId);
                // 缓存命中：反序列化JSON并立刻返回
                return objectMapper.readValue(cachedMenuJson, MenuVO.class);
            }
        } catch (JsonProcessingException e) {
            log.error("从缓存反序列化菜单失败，门店ID: {}", storeId, e);
        }

        // 3. 缓存未命中：从数据库构建菜单
        log.info("菜单缓存未命中，门店ID: {}。正在从数据库查询...", storeId);
        MenuVO menuFromDb = buildMenuFromDatabase(storeId);

        // 4. 将从数据库获取的结果存入缓存，以备下次使用
        try {
            String menuJson = objectMapper.writeValueAsString(menuFromDb);
            redisTemplate.opsForValue().set(cacheKey, menuJson, MENU_CACHE_TTL_MINUTES, TimeUnit.MINUTES);
            log.info("已成功填充菜单缓存，门店ID: {}", storeId);
        } catch (JsonProcessingException e) {
            log.error("序列化菜单以存入缓存失败，门店ID: {}", storeId, e);
        }

        // 5. 返回从数据库中构建的菜单
        return menuFromDb;
    }

    /**
     * 这个私有方法包含了原有的数据库查询和组装逻辑
     */
    private MenuVO buildMenuFromDatabase(Long storeId) {
        // 第1步: 查询该门店所有在售的SPU
        List<Spus> spusList = spusMapper.selectList(new QueryWrapper<Spus>()
                .eq("store_id", storeId)
                .eq("status", 1));

        if (spusList.isEmpty()) {
            MenuVO emptyMenu = new MenuVO();
            emptyMenu.setCategories(Collections.emptyList());
            return emptyMenu;
        }
        List<Long> spuIds = spusList.stream().map(Spus::getId).collect(Collectors.toList());

        // 第2步: 批量查询所有相关的SKU和Attribute
        List<Skus> skusList = skusMapper.selectList(new QueryWrapper<Skus>().in("spu_id", spuIds));
        Map<Long, List<Skus>> skusMap = skusList.stream().collect(Collectors.groupingBy(Skus::getSpuId));

        List<Attributes> attributesList = attributesMapper.selectList(new QueryWrapper<Attributes>().in("spu_id", spuIds));
        Map<Long, List<Attributes>> attributesMap = attributesList.stream().collect(Collectors.groupingBy(Attributes::getSpuId));

        // 第3步: 组装SpuVO
        List<MenuVO.SpuVO> spuVOs = spusList.stream().map(spu -> {
            MenuVO.SpuVO spuVO = new MenuVO.SpuVO();
            spuVO.setId(spu.getId());
            spuVO.setName(spu.getSpuName());
            spuVO.setDescription(spu.getSpuDescription());
            spuVO.setImageUrl(spu.getSpuImageUrl());
            spuVO.setCategoryName(spu.getCategoryName());

            // 组装SKU
            List<Skus> currentSkus = skusMap.getOrDefault(spu.getId(), Collections.emptyList());
            spuVO.setSkus(currentSkus.stream().map(sku -> {
                MenuVO.SkuVO skuVO = new MenuVO.SkuVO();
                skuVO.setId(sku.getId());
                skuVO.setName(sku.getSkuName());
                skuVO.setPrice(sku.getPrice());
                skuVO.setStock(sku.getStatus() == 1 ? sku.getStock() : 0);
                skuVO.setImageUrl(sku.getSkuImageUrl());
                skuVO.setSpecs(sku.getSpecs());
                return skuVO;
            }).collect(Collectors.toList()));

            // 组装Attribute
            List<Attributes> currentAttributes = attributesMap.getOrDefault(spu.getId(), Collections.emptyList());
            spuVO.setAttributes(currentAttributes.stream().map(attr -> {
                MenuVO.AttributeVO attrVO = new MenuVO.AttributeVO();
                attrVO.setName(attr.getAttributeName());
                attrVO.setOptions(attr.getOptions());
                return attrVO;
            }).collect(Collectors.toList()));

            return spuVO;
        }).collect(Collectors.toList());

        // 第4步: 按分类分组
        Map<String, List<MenuVO.SpuVO>> categorizedSpus = spuVOs.stream()
                .filter(spuVO -> spuVO.getSkus() != null && !spuVO.getSkus().isEmpty())
                .collect(Collectors.groupingBy(MenuVO.SpuVO::getCategoryName));

        // 第5步: 组装最终的MenuVO
        MenuVO menuVO = new MenuVO();
        menuVO.setCategories(categorizedSpus.entrySet().stream().map(entry -> {
            MenuVO.CategoryVO categoryVO = new MenuVO.CategoryVO();
            categoryVO.setName(entry.getKey());
            categoryVO.setSpus(entry.getValue());
            return categoryVO;
        }).collect(Collectors.toList()));

        return menuVO;
    }
}